package com.quectel.sdk.officialaccounts;

import cn.hutool.core.util.HexUtil;
import cn.hutool.crypto.digest.DigestUtil;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.request.AlipaySystemOauthTokenRequest;
import com.alipay.api.response.AlipaySystemOauthTokenResponse;
import com.fasterxml.jackson.core.type.TypeReference;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.quectel.sdk.officialaccounts.model.WechatJSAPIUserInfo;
import com.quectel.sdk.officialaccounts.model.WechatMsgPushVo;
import com.quectel.sdk.pay.wxv2.wxsdk.WXPayUtil;
import com.quectel.util.common.JacksonUtils;
import com.quectel.util.http.HttpRestUtils;
import com.quectel.util.kit.CacheKit;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;

/**
 * 公众号帮助类
 *
 * @author caolu
 * @email louis.cao@quectel.com
 */
public class OfficialAccountsUtils {

    private static final Logger LOGGER = LoggerFactory.getLogger(OfficialAccountsUtils.class);

    private static final HttpRestUtils HTTP_REST_UTILS = HttpRestUtils.getDefaultInstance();

    /**
     * 微信JSAPI票据缓存
     */
    private static final String WX_JS_API_TICKET_CACHE = "WX_JS_API_TICKET_CACHE:";

    /**
     * 微信accessCode缓存
     */
    private static final String WX_JS_API_ACCESS_CODE_CACHE = "WX_JS_API_ACCESS_CODE_CACHE:";

    /**
     * key appId
     * value 执行器
     */
    private static final Cache<String, DefaultAlipayClient> ALI_CLIENT_CACHE =
            CacheBuilder.newBuilder().expireAfterAccess(5, TimeUnit.MINUTES).build();


    /**
     * 得到微信JS-SDK初始化配置信息 用户调起摄像头 录音 图像等
     * wx.config({
     * debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来，若要查看传入的参数，可以在pc端打开，参数信息会通过log打出，仅在pc端时才会打印。
     * appId: '', // 必填，公众号的唯一标识
     * timestamp: , // 必填，生成签名的时间戳
     * nonceStr: '', // 必填，生成签名的随机串
     * signature: '',// 必填，签名
     * jsApiList: [] // 必填，需要使用的JS接口列表
     * });
     * 我们这里不返回 debug jsApiList 由前端控制
     *
     * @param currentPageUrl 当前网页路径 前端传入
     * @return
     */
    public static Map<String, String> getWxJSSDKConfig(String appId, String appSecret, String currentPageUrl) {

        String jsApiTicket = getWxJsApiTicket(appId, appSecret);

        Map<String, String> ret = new HashMap<String, String>();
        String nonceStr = WXPayUtil.generateNonceStr();
        String timestamp = WXPayUtil.getCurrentTimestamp() + "";

        //注意这里参数名必须全部小写，且必须有序
        String concatStr = "jsapi_ticket=" + jsApiTicket +
                "&noncestr=" + nonceStr +
                "&timestamp=" + timestamp +
                "&url=" + currentPageUrl;

        byte[] bytes = DigestUtil.sha1(concatStr);
        String signature = HexUtil.encodeHexStr(bytes);

        ret.put("appId", appId);
        ret.put("nonceStr", nonceStr);
        ret.put("timestamp", timestamp);
        ret.put("signature", signature);
        return ret;
    }

    /**
     * 获取微信票据
     */
    public static String getWxJsApiTicket(String appId, String appSecret) {
        return CacheKit.cacheToRedis(
                () -> {
                    String accessToken = getWxJsApiAccessToken(appId, appSecret);
                    String requestUrl = String.format(
                            "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=%s&type=jsapi",
                            accessToken
                    );
                    HashMap<String, Object> respParams = HTTP_REST_UTILS.get(requestUrl, null, null, new TypeReference<HashMap<String, Object>>() {
                    }).getValue();
                    if (respParams.containsKey("ticket")) {
                        //默认是7200s我们缓存7000秒
                        return (String) respParams.get("ticket");
                    }
                    return null;
                },
                WX_JS_API_TICKET_CACHE + appId,
                7000
        );

    }

    /**
     * 获取微信accessToken
     *
     * @return
     */
    public static String getWxJsApiAccessToken(String appId, String appSecret) {
        return CacheKit.cacheToRedis(
                () -> {
                    String requestUrl = String.format(
                            "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s",
                            appId,
                            appSecret
                    );
                    HashMap respParams = (HashMap) HTTP_REST_UTILS.get(requestUrl, null, null, new TypeReference<HashMap<String, Object>>() {
                    }).getValue();
                    if (respParams.containsKey("access_token")) {
                        String accessToken = (String) respParams.get("access_token");
                        //默认是7200s我们缓存7000秒
                        return accessToken;
                    }
                    return "null";
                },
                WX_JS_API_ACCESS_CODE_CACHE + appId,
                7000
        );

    }

    /**
     * 根据openId获取微信用户详细信息
     *
     * @param appId
     * @param appSecret
     * @param openId
     * @return
     */
    public static WechatJSAPIUserInfo getWxUserInfoDetail(String appId, String appSecret, String openId) {

        String requestUrl = String.format("https://api.weixin.qq.com/cgi-bin/user/info?access_token=%s&openid=%s&lang=zh_CN",
                getWxJsApiAccessToken(appId, appSecret), openId);

        String responseStr = (String) HTTP_REST_UTILS.get(requestUrl, null, null, new TypeReference<String>() {
        }).getRight();
        if (StringUtils.isNoneBlank(responseStr)) {
            return JacksonUtils.parse(responseStr, WechatJSAPIUserInfo.class);
        }
        return null;
    }


    /**
     * 获取微信JSAPI上的用户唯一标识
     *
     * @param appId
     * @param appSecret
     * @param authCode
     * @return openId
     */
    public static String getWxJsApiUserId(String appId, String appSecret, String authCode) {

        String requestUrl = String.format(
                "https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code",
                appId,
                appSecret,
                authCode
        );
        String responseStr = (String) HTTP_REST_UTILS.get(requestUrl, null, null, new TypeReference<String>() {
        }).getRight();
        if (StringUtils.isNoneBlank(responseStr)) {
            Map<String, Object> recData = JacksonUtils.parse(responseStr, new TypeReference<Map<String, Object>>() {
            });
            return (String) recData.get("openid");
        }
        return null;
    }

    /**
     * 微信发送消息
     *
     * @param appId
     * @param appSecret
     * @param vo        消息信息
     * @return
     */
    public static void sendWechatMsgToUser(String appId, String appSecret, WechatMsgPushVo vo) {

        String wxJsApiAccessToken = getWxJsApiAccessToken(appId, appSecret);

        String requestUrl = String.format(
                "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=%s",
                wxJsApiAccessToken
        );
        Map<String, Object> bodyParams = new HashMap<>();
        bodyParams.put("touser", vo.getToUser());
        bodyParams.put("template_id", vo.getTemplateId());
        bodyParams.put("url", vo.getLinkUrl());
        bodyParams.put("topcolor", "#FF0000");
        Map<String, Object> dataParams = new HashMap<>();
        if (vo.getParams() != null) {
            for (Map.Entry<String, String> entry : vo.getParams().entrySet()) {
                Map<String, Object> itemParams = new HashMap<>();
                itemParams.put("value", entry.getValue());
                itemParams.put("color", "#173177");
                dataParams.put(entry.getKey(), itemParams);
            }
        }
        bodyParams.put("data", dataParams);
        String responseStr = (String) HTTP_REST_UTILS.post(requestUrl, bodyParams, null, new TypeReference<String>() {
        }).getRight();
        LOGGER.debug("发送微信响应消息:{}", responseStr);
    }


    /**
     * 得到支付宝用户唯一标识
     *
     * @param appId
     * @param zfbPublicKey   支付宝公钥
     * @param selfPrivateKey 支付宝私钥
     * @param authCode       authCode
     * @return
     */
    public static String getZfbUserId(String appId, String zfbPublicKey, String selfPrivateKey, String authCode) {

        try {
            DefaultAlipayClient defaultAlipayClient = ALI_CLIENT_CACHE.get(appId, new Callable<DefaultAlipayClient>() {
                @Override
                public DefaultAlipayClient call() throws Exception {
                    return new DefaultAlipayClient(
                            "https://openapi.alipay.com/gateway.do",
                            appId,
                            selfPrivateKey,
                            "json",
                            "UTF-8",
                            zfbPublicKey,
                            "RSA2");
                }
            });
            AlipaySystemOauthTokenRequest request = new AlipaySystemOauthTokenRequest();
            request.setCode(authCode);
            request.setGrantType("authorization_code");
            AlipaySystemOauthTokenResponse oauthTokenResponse = defaultAlipayClient.execute(request);
            return oauthTokenResponse.getUserId();
        } catch (Exception e) {
            LOGGER.error("Obtain zfb user info error", e);
        }

        return null;
    }


}
